package network

import (
	"fmt"
	"go_learn/mlog"
	"net"
	"sync"

	prb "go_learn/pool/ringbuffer"
	"go_learn/ringbuffer"
)

type TCPConn struct {
	sync.Mutex
	conn       net.Conn
	writeChan  chan []byte
	closeFlag  bool
	codec      ICodec
	buffer     []byte
	readBuffer []byte                 // reuse memory of inbound data as a temporary buffer
	ringBuffer *ringbuffer.RingBuffer // buffer for data from client
}

func newTCPConn(conn net.Conn, pendingWriteNum int, codec ICodec) *TCPConn {
	c := new(TCPConn)
	c.conn = conn
	c.writeChan = make(chan []byte, pendingWriteNum)
	c.codec = codec
	c.ringBuffer = prb.Get()
	c.buffer = make([]byte, 1024*4)
	go c.SendProc()
	return c
}

func (c *TCPConn) SendProc() {
	for b := range c.writeChan {
		if b == nil {
			break
		}
		err := c.doWrite(b)
		if err != nil {
			mlog.Errorf("send msg error %s", err.Error())
			break
		}
	}
	c.Close()
	c.Lock()
	c.closeFlag = true
	c.Unlock()
}

func (c *TCPConn) doDestroy() {
	c.conn.(*net.TCPConn).SetLinger(0)
	c.conn.Close()
	c.buffer = nil
	prb.Put(c.ringBuffer)
	c.ringBuffer = ringbuffer.EmptyRingBuffer
	if !c.closeFlag {
		close(c.writeChan)
		c.closeFlag = true
	}
}

func (tcpConn *TCPConn) Destroy() {
	tcpConn.Lock()
	defer tcpConn.Unlock()

	tcpConn.doDestroy()
}

func (c *TCPConn) Close() error {
	c.Lock()
	defer c.Unlock()
	if c.closeFlag {
		return fmt.Errorf("close flag is true")
	}
	if len(c.writeChan) == cap(c.writeChan) {
		c.doDestroy()
		return nil
	}
	c.writeChan <- nil
	c.closeFlag = true
	return nil
}

func (c *TCPConn) Read() (buf []byte) {
	head, tail := c.ringBuffer.LazyReadAll()
	buf = make([]byte, 0, c.BufferLength())
	buf = append(buf, head...)
	buf = append(buf, tail...)
	buf = append(buf, c.readBuffer...)
	return buf
}

func (c *TCPConn) ResetBuffer() {
	c.readBuffer = c.readBuffer[:0]
	c.ringBuffer.Reset()
}

func (c *TCPConn) ReadN(n int) (size int, buf []byte) {
	if n > c.BufferLength() {
		n = c.BufferLength()
	}
	head, tail := c.ringBuffer.LazyRead(n)
	buf = make([]byte, 0, n)
	buf = append(buf, head...)
	buf = append(buf, tail...)
	buf = append(buf, c.readBuffer...)
	return n, buf
}

func (c *TCPConn) ShiftN(n int) (size int) {
	inBufferLen := c.ringBuffer.Length()
	tempBufferLen := len(c.readBuffer)
	if inBufferLen+tempBufferLen < n || n <= 0 {
		c.ResetBuffer()
		size = inBufferLen + tempBufferLen
		return
	}
	size = n
	if c.ringBuffer.IsEmpty() {
		c.readBuffer = c.readBuffer[n:]
		return
	}

	if inBufferLen >= n {
		c.ringBuffer.Shift(n)
		return
	}
	c.ringBuffer.Reset()

	restSize := n - inBufferLen
	c.readBuffer = c.readBuffer[restSize:]
	return
}

func (c *TCPConn) doWrite(buf []byte) error {
	msg, err := c.PackMsg(buf)
	if err != nil {
		mlog.Errorf("pack msg fail %s", err.Error())
		return err
	}
	_, err = c.conn.Write(msg)
	if err != nil {
		mlog.Errorf("write msg fail %s", err.Error())
		return err
	}
	return nil
}

func (c *TCPConn) SyncWrite(buf []byte) error {
	c.Lock()
	defer c.Unlock()
	if c.closeFlag || buf == nil { //有可能在已关闭的情况下chan中还有堆积
		return nil
	}
	return c.doWrite(buf)
}

func (c *TCPConn) AsyncWrite(buf []byte) (err error) {
	c.Lock()
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case error:
				err = r.(error)
			default:
				err = fmt.Errorf("%v", r)
			}
		}
		c.Unlock()
	}()
	if c.closeFlag || buf == nil {
		return nil
	}
	if len(c.writeChan) == cap(c.writeChan) {
		return fmt.Errorf("write chan is full")
	}
	c.writeChan <- buf
	return
}

func (c *TCPConn) ReadPackge() (b []byte, err error) {
	for {
		if c.BufferLength() != 0 {
			b, err = c.codec.Decode(c)
			if err != nil || b != nil {
				return
			}
		}
		var n int
		n, err = c.conn.Read(c.buffer)
		c.readBuffer = c.buffer[:n]
		if n == 0 || err != nil {
			return
		}
		if c.BufferLength() != 0 {
			b, err = c.codec.Decode(c)
			if err != nil || b != nil {
				mlog.Debugf("recv msg %s", string(b))
				return
			}
		}
		_, err = c.ringBuffer.Write(c.readBuffer)
		if err != nil {
			return
		}
	}
}

func (c *TCPConn) PackMsg(buf []byte) (b []byte, err error) {
	return c.codec.Encode(c, buf)
}

func (c *TCPConn) BufferLength() (size int) {
	return c.ringBuffer.Length() + len(c.readBuffer)
}

func (c *TCPConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

func (c *TCPConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}
